using System;
using System.Globalization;

namespace Altova.Types
{
	public class DateTime : IComparable
	{
		[Flags]
		public enum DateTimePart
		{
			Year = 1,
			Month = 2,
			Day = 4,
			Date = 7,
			Hour = 8,
			Minute = 0x10,
			Second = 0x20,
			Time = 0x38,
			TimezoneHour = 0x40,
			TimezoneMinute = 0x80,
			Timezone = 0xC0
		}

		public class ParseContext
		{
			private string source;

			private int index;

			public int Index
			{
				get
				{
					return index;
				}
			}

			public ParseContext(string source)
			{
				this.source = source;
				index = 0;
			}

			public bool IsValid()
			{
				return index < source.Length;
			}

			public bool Check(char expect)
			{
				if (!IsValid())
				{
					return false;
				}
				return source[index] == expect;
			}

			public bool CheckAndAdvance(char expect)
			{
				if (!Check(expect))
				{
					return false;
				}
				Advance();
				return true;
			}

			public void Advance()
			{
				index++;
			}

			public bool ReadDigitAndAdvance(ref int value, int scale, int maxdigit)
			{
				if (!IsValid())
				{
					return false;
				}
				char c = source[index];
				if (c < '0' || c > '9')
				{
					return false;
				}
				int num = c - 48;
				if (num > maxdigit)
				{
					return false;
				}
				value += num * scale;
				Advance();
				return true;
			}
		}

		public const short NoTimezone = short.MinValue;

		private System.DateTime myValue;

		private short timezone = short.MinValue;

		public static readonly long TicksPerSecond = 10000000L;

		public System.DateTime Value
		{
			get
			{
				return myValue;
			}
			set
			{
				myValue = value;
			}
		}

		public bool HasTimezone
		{
			get
			{
				return timezone != short.MinValue;
			}
		}

		public short TimezoneOffset
		{
			get
			{
				return timezone;
			}
			set
			{
				timezone = value;
			}
		}

		public static DateTime Now
		{
			get
			{
				System.DateTime now = System.DateTime.Now;
				TimeSpan utcOffset = TimeZone.CurrentTimeZone.GetUtcOffset(now);
				DateTime dateTime = new DateTime(now);
				dateTime.TimezoneOffset = (short)utcOffset.TotalMinutes;
				return dateTime;
			}
		}

		public override bool Equals(object obj)
		{
			if (!(obj is DateTime))
			{
				return false;
			}
			DateTime dateTime = (DateTime)obj;
			if (myValue == dateTime.myValue)
			{
				return timezone == dateTime.timezone;
			}
			return false;
		}

		public override int GetHashCode()
		{
			return myValue.GetHashCode() ^ timezone.GetHashCode();
		}

		public int CompareTo(object obj)
		{
			if (obj == null)
			{
				return 1;
			}
			if (!(obj is DateTime))
			{
				throw new ArgumentException("Argument is not of type DateTime");
			}
			DateTime dateTime = (DateTime)obj;
			if (timezone == short.MinValue && dateTime.timezone != short.MinValue)
			{
				return 0;
			}
			if (timezone != short.MinValue && dateTime.timezone == short.MinValue)
			{
				return 0;
			}
			if (timezone == short.MinValue)
			{
				return myValue.CompareTo(dateTime.myValue);
			}
			System.DateTime dateTime2 = myValue;
			System.DateTime value = dateTime.myValue;
			dateTime2.AddMinutes(-timezone);
			value.AddMinutes(-dateTime.timezone);
			return dateTime2.CompareTo(value);
		}

		public bool ParseDateTime(string s, DateTimePart part)
		{
			return ParseDateTime(s, part, (DateTimePart)0);
		}

		public bool ParseDateTime(string s, DateTimePart part, DateTimePart optional)
		{
			ParseContext parseContext = new ParseContext(s.Trim());
			bool flag = (part & DateTimePart.Date) != 0;
			bool flag2 = (part & DateTimePart.Time) != 0;
			int num = 0;
			int value = 0;
			int value2 = 0;
			int value3 = 0;
			int value4 = 0;
			double num2 = 0.0;
			timezone = short.MinValue;
			if (flag)
			{
				bool flag3 = parseContext.CheckAndAdvance('-');
				if ((part & DateTimePart.Year) != 0)
				{
					int num3 = 0;
					int value5 = 0;
					while (parseContext.ReadDigitAndAdvance(ref value5, 1, 9))
					{
						num = num * 10 + value5;
						num3++;
						value5 = 0;
						if (num3 >= 8)
						{
							return false;
						}
					}
					if (num3 < 4)
					{
						return false;
					}
					if (num3 > 4 && num < 10000)
					{
						return false;
					}
					if (flag3)
					{
						num = -num;
					}
				}
				if ((part & (DateTimePart.Month | DateTimePart.Day)) != 0)
				{
					if (!parseContext.CheckAndAdvance('-'))
					{
						return false;
					}
					if ((part & DateTimePart.Month) != 0)
					{
						if (!parseContext.ReadDigitAndAdvance(ref value, 10, 1))
						{
							return false;
						}
						if (!parseContext.ReadDigitAndAdvance(ref value, 1, (value < 10) ? 9 : 2))
						{
							return false;
						}
						if (value == 0)
						{
							return false;
						}
					}
					if ((part & DateTimePart.Day) != 0)
					{
						if (!parseContext.CheckAndAdvance('-'))
						{
							return false;
						}
						int maxdigit = ((value != 2) ? 3 : 2);
						if (!parseContext.ReadDigitAndAdvance(ref value2, 10, maxdigit))
						{
							return false;
						}
						if (!parseContext.ReadDigitAndAdvance(ref value2, 1, 9))
						{
							return false;
						}
						if (value2 == 0 || value2 > 31)
						{
							return false;
						}
						if ((part & DateTimePart.Month) != 0)
						{
							bool flag4 = value <= 7;
							bool flag5 = (value & 1) == 0;
							if (flag4 == flag5 && value2 > 30)
							{
								return false;
							}
							if (value == 2 && value2 > 29)
							{
								return false;
							}
							if (value == 2 && (part & DateTimePart.Year) != 0 && (num % 4 != 0 || num % 100 == 0) && num % 400 != 0 && value2 > 28)
							{
								return false;
							}
						}
					}
				}
				if (flag2 && !parseContext.CheckAndAdvance('T'))
				{
					return false;
				}
			}
			if (flag2)
			{
				if (!parseContext.ReadDigitAndAdvance(ref value3, 10, 2))
				{
					return false;
				}
				if (!parseContext.ReadDigitAndAdvance(ref value3, 1, (value3 < 20) ? 9 : 4))
				{
					return false;
				}
				if (!parseContext.CheckAndAdvance(':'))
				{
					return false;
				}
				int maxdigit2 = ((value3 != 24) ? 5 : 0);
				int maxdigit3 = ((value3 != 24) ? 9 : 0);
				if (!parseContext.ReadDigitAndAdvance(ref value4, 10, maxdigit2))
				{
					return false;
				}
				if (!parseContext.ReadDigitAndAdvance(ref value4, 1, maxdigit3))
				{
					return false;
				}
				if (!parseContext.CheckAndAdvance(':'))
				{
					return false;
				}
				int value6 = 0;
				if (!parseContext.ReadDigitAndAdvance(ref value6, 10, maxdigit2))
				{
					return false;
				}
				if (!parseContext.ReadDigitAndAdvance(ref value6, 1, maxdigit3))
				{
					return false;
				}
				num2 = value6;
				if (parseContext.CheckAndAdvance('.'))
				{
					int value7 = 0;
					int num4 = 0;
					while (parseContext.ReadDigitAndAdvance(ref value7, 1, 9))
					{
						value7 *= 10;
						num4++;
						if (num4 >= 8)
						{
							break;
						}
					}
					if (num4 == 0)
					{
						return false;
					}
					num2 += (double)value7 * Math.Pow(10.0, -num4 - 1);
					while (parseContext.ReadDigitAndAdvance(ref value7, 0, 9))
					{
					}
				}
			}
			if (parseContext.CheckAndAdvance('Z'))
			{
				timezone = 0;
			}
			else if (parseContext.Check('+') || parseContext.Check('-'))
			{
				bool flag6 = parseContext.Check('-');
				parseContext.Advance();
				int value8 = 0;
				bool flag7 = false;
				if (!parseContext.ReadDigitAndAdvance(ref value8, 600, 9))
				{
					return false;
				}
				if (!parseContext.ReadDigitAndAdvance(ref value8, 60, 9))
				{
					return false;
				}
				if (!parseContext.CheckAndAdvance(':'))
				{
					if ((optional & DateTimePart.TimezoneMinute) == 0)
					{
						return false;
					}
					flag7 = true;
				}
				if (!flag7 && !parseContext.ReadDigitAndAdvance(ref value8, 10, 5))
				{
					return false;
				}
				if (!flag7 && !parseContext.ReadDigitAndAdvance(ref value8, 1, 9))
				{
					return false;
				}
				timezone = (short)(flag6 ? (-value8) : value8);
			}
			if (parseContext.IsValid())
			{
				return false;
			}
			if (num <= 0)
			{
				num = 1;
			}
			if (value == 0)
			{
				value = 1;
			}
			bool flag8 = false;
			if (value3 == 24)
			{
				value3 = 0;
				flag8 = true;
			}
			if (value2 == 0)
			{
				value2 = 1;
			}
			if ((part & DateTimePart.Year) == 0 && value == 2 && value2 == 29)
			{
				num = 4;
			}
			try
			{
				myValue = new System.DateTime(num, value, value2, value3, value4, (int)num2, (int)(num2 * 1000.0) % 1000);
				if (flag8)
				{
					myValue = myValue.AddDays(1.0);
				}
			}
			catch
			{
				return false;
			}
			return true;
		}

		public static DateTime Parse(string s, DateTimeFormat format)
		{
			bool flag = false;
			DateTime dateTime = new DateTime();
			switch (format)
			{
			case DateTimeFormat.W3_dateTime:
				flag = dateTime.ParseDateTime(s, DateTimePart.Date | DateTimePart.Time);
				break;
			case DateTimeFormat.W3_date:
				flag = dateTime.ParseDateTime(s, DateTimePart.Date);
				break;
			case DateTimeFormat.W3_time:
				flag = dateTime.ParseDateTime(s, DateTimePart.Time);
				break;
			case DateTimeFormat.W3_gYear:
				flag = dateTime.ParseDateTime(s, DateTimePart.Year);
				break;
			case DateTimeFormat.W3_gYearMonth:
				flag = dateTime.ParseDateTime(s, DateTimePart.Year | DateTimePart.Month);
				break;
			case DateTimeFormat.W3_gMonth:
				flag = dateTime.ParseDateTime(s, DateTimePart.Month);
				break;
			case DateTimeFormat.W3_gMonthDay:
				flag = dateTime.ParseDateTime(s, DateTimePart.Month | DateTimePart.Day);
				break;
			case DateTimeFormat.W3_gDay:
				flag = dateTime.ParseDateTime(s, DateTimePart.Day);
				break;
			}
			if (!flag)
			{
				throw new StringParseException(s + " cannot be converted to a dateTime value");
			}
			return dateTime;
		}

		public DateTime()
		{
		}

		public DateTime(DateTime obj)
		{
			myValue = obj.myValue;
			timezone = obj.timezone;
		}

		public DateTime(System.DateTime newvalue)
		{
			myValue = newvalue;
			timezone = short.MinValue;
		}

		public DateTime(int year, int month, int day, int hour, int minute, double second, int offsetTZ)
		{
			myValue = new System.DateTime(year, month, day, hour, minute, 0);
			myValue = myValue.AddSeconds(second);
			timezone = (short)offsetTZ;
		}

		public DateTime(int year, int month, int day, int hour, int minute, double second)
			: this(year, month, day, hour, minute, second, -32768)
		{
		}

		public DateTime(int year, int month, int day)
			: this(year, month, day, 0, 0, 0.0, -32768)
		{
		}

		private string doTimezone()
		{
			short num = timezone;
			switch (num)
			{
			case 0:
				return "Z";
			default:
			{
				string text = "+";
				if (num < 0)
				{
					num = (short)(-num);
					text = "-";
				}
				int num2 = num / 60;
				int num3 = num % 60;
				return text + num2.ToString("00") + ":" + num3.ToString("00");
			}
			case short.MinValue:
				return "";
			}
		}

		private string killFraction(string s)
		{
			return s.TrimEnd('0').TrimEnd('.');
		}

		public string ToString(DateTimeFormat format)
		{
			switch (format)
			{
			case DateTimeFormat.W3_dateTime:
				return killFraction(myValue.ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffffff", CultureInfo.InvariantCulture)) + doTimezone();
			case DateTimeFormat.W3_date:
				return myValue.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) + doTimezone();
			case DateTimeFormat.W3_time:
				return killFraction(myValue.ToString("HH\\:mm\\:ss.fffffff", CultureInfo.InvariantCulture)) + doTimezone();
			case DateTimeFormat.W3_gYear:
				return myValue.ToString("yyyy", CultureInfo.InvariantCulture) + doTimezone();
			case DateTimeFormat.W3_gYearMonth:
				return myValue.ToString("yyyy-MM", CultureInfo.InvariantCulture) + doTimezone();
			case DateTimeFormat.W3_gMonth:
				return myValue.ToString("--MM", CultureInfo.InvariantCulture) + doTimezone();
			case DateTimeFormat.W3_gMonthDay:
				return myValue.ToString("--MM-dd", CultureInfo.InvariantCulture) + doTimezone();
			case DateTimeFormat.W3_gDay:
				return myValue.ToString("---dd", CultureInfo.InvariantCulture) + doTimezone();
			case DateTimeFormat.S_DateTime:
				return myValue.ToString("yyyy-MM-dd HH\\:mm\\:ss.fffffff", CultureInfo.InvariantCulture) + doTimezone();
			case DateTimeFormat.S_Seconds:
			{
				decimal num2 = myValue.Ticks;
				return killFraction((num2 / (decimal)TicksPerSecond).ToString());
			}
			case DateTimeFormat.S_Days:
			{
				decimal num = myValue.Ticks;
				return killFraction((num / (decimal)(86400 * TicksPerSecond)).ToString());
			}
			default:
				throw new ArgumentException("Invalid format.");
			}
		}

		public int GetWeekOfMonth()
		{
			DateTime dateTime = new DateTime(myValue.Year, myValue.Month, 1);
			int dayOfWeek = (int)dateTime.Value.DayOfWeek;
			dayOfWeek = ((dayOfWeek == 0) ? 7 : (dayOfWeek - 1));
			int[] array = new int[6]
			{
				0,
				7 - dayOfWeek,
				0,
				0,
				0,
				0
			};
			for (int i = 2; i < 6; i++)
			{
				array[i] = array[i - 1] + 7;
			}
			int num = 5;
			while (myValue.Day <= array[num])
			{
				num--;
			}
			return num + 1;
		}

		public System.DateTime GetDateTime(bool correctTZ)
		{
			System.DateTime result = myValue;
			if (correctTZ && timezone != short.MinValue)
			{
				result.AddMinutes(-timezone);
			}
			return result;
		}

		public static DateTime Parse(string s)
		{
			string text = s.Trim();
			DateTime dateTime = new DateTime();
			if (text != null && !dateTime.ParseDateTime(text, DateTimePart.Date | DateTimePart.Time) && !dateTime.ParseDateTime(text, DateTimePart.Date) && !dateTime.ParseDateTime(text, DateTimePart.Time) && !dateTime.ParseDateTime(text, DateTimePart.Year | DateTimePart.Month) && !dateTime.ParseDateTime(text, DateTimePart.Month | DateTimePart.Day) && !dateTime.ParseDateTime(text, DateTimePart.Year) && !dateTime.ParseDateTime(text, DateTimePart.Month) && !dateTime.ParseDateTime(text, DateTimePart.Day))
			{
				throw new StringParseException(text + " cannot be converted to a dateTime value");
			}
			return dateTime;
		}

		public override string ToString()
		{
			return ToString(DateTimeFormat.W3_dateTime);
		}

		public static bool operator ==(DateTime a, DateTime b)
		{
			if (object.ReferenceEquals(a, null))
			{
				return object.ReferenceEquals(b, null);
			}
			return a.Equals(b);
		}

		public static bool operator !=(DateTime a, DateTime b)
		{
			return !(a == b);
		}

		public static bool operator <(DateTime a, DateTime b)
		{
			if (object.ReferenceEquals(a, null))
			{
				return !object.ReferenceEquals(b, null);
			}
			if (object.ReferenceEquals(b, null))
			{
				return false;
			}
			return a.CompareTo(b) < 0;
		}

		public static bool operator >=(DateTime a, DateTime b)
		{
			return !(a < b);
		}

		public static bool operator <=(DateTime a, DateTime b)
		{
			return !(b < a);
		}

		public static bool operator >(DateTime a, DateTime b)
		{
			return b < a;
		}
	}
}
